package lru

import "container/list"

type Cache struct {
	// 允许使用的最大缓存数
	maxBytes int64

	// 当前已使用的内存
	nbytes int64

	// 双向链
	ll *list.List

	// 定义字典
	cache map[string]*list.Element

	// 某条记录被移除时的回调函数
	OnEvicted func(key string, value Value)
}

type entry struct {
	key   string
	value Value
}

// Value use Len to count how many bytes it takes
type Value interface {
	Len() int
}

// 实例化Cache
func New(maxBytes int64, onEvicted func(string, Value)) *Cache {
	return &Cache{
		maxBytes:  maxBytes,
		ll:        list.New(),
		cache:     make(map[string]*list.Element),
		OnEvicted: onEvicted,
	}
}

func (c *Cache) Get(key string) (value Value, ok bool) {
	if ele, ok := c.cache[key]; ok {
		c.ll.MoveToFront(ele)
		kv := ele.Value.(*entry)
		return kv.value, true
	}
	return
}

func (c *Cache) RemoveOldest() {
	// 选择链表中的最后一个element
	ele := c.ll.Back()

	if ele != nil {
		// 链表中进行删除
		c.ll.Remove(ele)

		// 在字典中进行删除
		kv := ele.Value.(*entry)
		delete(c.cache, kv.key)

		// 更新当前已使用的内存
		c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len())

		// 调用回调函数，具体有什么作用后面会展示
		if c.OnEvicted != nil {
			c.OnEvicted(kv.key, kv.value)
		}
	}
}

// 新增或修改
func (c *Cache) Add(key string, value Value) {
	if ele, ok := c.cache[key]; ok {
		// 如果是修改，将这个缓存对象移动到队列最前方
		c.ll.MoveToFront(ele)

		// 对新增对象进行类型断言，保证是entry类型
		kv := ele.Value.(*entry)
		// 更新内存大小
		c.nbytes += int64(value.Len()) - int64(kv.value.Len())
		// 更新value
		kv.value = value
	} else {
		// 如果新增， 则在链表中新增这个缓存对象
		// 并将其放在链表的最前方
		ele := c.ll.PushFront(&entry{key, value})

		// 记录在字典中
		c.cache[key] = ele

		// 更新缓存大小
		c.nbytes += int64(len(key)) + int64(value.Len())
	}

	// 当超过最大缓存时，执行RemoveOldest 淘汰掉相应的缓存对象
	// 这里使用了for循环来完成这个功能
	for c.maxBytes != 0 && c.maxBytes < c.nbytes {
		c.RemoveOldest()
	}
}
